Skip to content

[Clang][NFC] Enumerate Clang ABI versions in a separate header file #151995

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Aug 7, 2025

Conversation

zwuis
Copy link
Contributor

@zwuis zwuis commented Aug 4, 2025

Make it easier for us to add ABI versions.

Close #144332

@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" labels Aug 4, 2025
@llvmbot
Copy link
Member

llvmbot commented Aug 4, 2025

@llvm/pr-subscribers-clang

Author: Yanzuo Liu (zwuis)

Changes

Make it easier for us to add abi versions.

Close #144332


Full diff: https://github.com/llvm/llvm-project/pull/151995.diff

3 Files Affected:

  • (added) clang/include/clang/Basic/ABIVersions.def (+127)
  • (modified) clang/include/clang/Basic/LangOptions.h (+4-88)
  • (modified) clang/lib/Frontend/CompilerInvocation.cpp (+18-63)
diff --git a/clang/include/clang/Basic/ABIVersions.def b/clang/include/clang/Basic/ABIVersions.def
new file mode 100644
index 0000000000000..57940544b104d
--- /dev/null
+++ b/clang/include/clang/Basic/ABIVersions.def
@@ -0,0 +1,127 @@
+//===--- ABIVersions.def - Clang ABI Versions Database ----------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file enumerates Clang ABI versions.
+//
+//===----------------------------------------------------------------------===//
+//
+/// @file ABIVersions.def
+///
+/// In this file, each of the Clang ABI Versions except "latest" is enumerated
+/// ABI_VER_MAJOR_MINOR, or ABI_VER_MAJOR macro.
+///
+/// ABI_VER_MAJOR is used when the minor version is 0 or can be omitted.
+///
+/// The first argument of ABI_VER_MAJOR_MINOR and ABI_VER_MAJOR is the major
+/// version.
+///
+/// The second argument of ABI_VER_MAJOR_MINOR is the minor version.
+
+#if defined(ABI_VER_MAJOR_MINOR) != defined(ABI_VER_MAJOR)
+#  error ABI_VER_MAJOR_MINOR and ABI_VER_MAJOR should be defined simultaneously
+#endif
+
+#ifndef ABI_VER_MAJOR_MINOR
+#  define ABI_VER_MAJOR_MINOR(Major, Minor)
+#endif
+
+#ifndef ABI_VER_MAJOR
+#  define ABI_VER_MAJOR(Major)
+#endif
+
+/// Attempt to be ABI-compatible with code generated by Clang 3.8.x
+/// (SVN r257626). This causes <1 x long long> to be passed in an integer
+/// register instead of an SSE register on x64_64.
+ABI_VER_MAJOR_MINOR(3, 8)
+
+/// Attempt to be ABI-compatible with code generated by Clang 4.0.x
+/// (SVN r291814). This causes move operations to be ignored when determining
+/// whether a class type can be passed or returned directly.
+ABI_VER_MAJOR(4)
+
+/// Attempt to be ABI-compatible with code generated by Clang 6.0.x
+/// (SVN r321711). This causes determination of whether a type is
+/// standard-layout to ignore collisions between empty base classes and between
+/// base classes and member subobjects, which affects whether we reuse base
+/// class tail padding in some ABIs.
+ABI_VER_MAJOR(6)
+
+/// Attempt to be ABI-compatible with code generated by Clang 7.0.x
+/// (SVN r338536). This causes alignof (C++) and _Alignof (C11) to be compatible
+/// with __alignof (i.e., return the preferred alignment) rather than returning
+/// the required alignment.
+ABI_VER_MAJOR(7)
+
+/// Attempt to be ABI-compatible with code generated by Clang 9.0.x
+/// (SVN r351319). This causes vectors of __int128 to be passed in memory
+/// instead of passing in multiple scalar registers on x86_64 on Linux and
+/// NetBSD.
+ABI_VER_MAJOR(9)
+
+/// Attempt to be ABI-compatible with code generated by Clang 11.0.x
+/// (git 2e10b7a39b93). This causes clang to pass unions with a 256-bit vector
+/// member on the stack instead of using registers, to not properly mangle
+/// substitutions for template names in some cases, and to mangle declaration
+/// template arguments without a cast to the parameter type even when that can
+/// lead to mangling collisions.
+ABI_VER_MAJOR(11)
+
+/// Attempt to be ABI-compatible with code generated by Clang 12.0.x
+/// (git 8e464dd76bef). This causes clang to mangle lambdas within global-scope
+/// inline variables incorrectly.
+ABI_VER_MAJOR(12)
+
+/// Attempt to be ABI-compatible with code generated by Clang 14.0.x.
+/// This causes clang to:
+///   - mangle dependent nested names incorrectly.
+///   - make trivial only those defaulted copy constructors with a
+///     parameter-type-list equivalent to the parameter-type-list of an implicit
+///     declaration.
+ABI_VER_MAJOR(14)
+
+/// Attempt to be ABI-compatible with code generated by Clang 15.0.x.
+/// This causes clang to:
+///   - Reverse the implementation for CWG692, CWG1395 and CWG1432.
+///   - pack non-POD members of packed structs.
+///   - consider classes with defaulted special member functions non-pod.
+ABI_VER_MAJOR(15)
+
+/// Attempt to be ABI-compatible with code generated by Clang 17.0.x.
+/// This causes clang to revert some fixes to its implementation of the Itanium
+/// name mangling scheme, with the consequence that overloaded function
+/// templates are mangled the same if they differ only by:
+///   - constraints
+///   - whether a non-type template parameter has a deduced type
+///   - the parameter list of a template template parameter
+ABI_VER_MAJOR(17)
+
+/// Attempt to be ABI-compatible with code generated by Clang 18.0.x.
+/// This causes clang to revert some fixes to the mangling of lambdas in the
+/// initializers of members of local classes.
+ABI_VER_MAJOR(18)
+
+/// Attempt to be ABI-compatible with code generated by Clang 19.0.x.
+/// This causes clang to:
+///   - Incorrectly mangle the 'base type' substitutions of the CXX construction
+///     vtable because it hasn't added 'type' as a substitution.
+///   - Skip mangling enclosing class templates of member-like friend function
+///     templates.
+///   - Ignore empty struct arguments in C++ mode for ARM, instead of passing
+///     them as if they had a size of 1 byte.
+ABI_VER_MAJOR(19)
+
+/// Attempt to be ABI-compatible with code generated by Clang 20.0.x.
+/// This causes clang to:
+///   - Incorrectly return C++ records in AVX registers on x86_64.
+ABI_VER_MAJOR(20)
+
+/// Conform to the underlying platform's C and C++ ABIs as closely as we can.
+// (latest)
+
+#undef ABI_VER_MAJOR_MINOR
+#undef ABI_VER_MAJOR
diff --git a/clang/include/clang/Basic/LangOptions.h b/clang/include/clang/Basic/LangOptions.h
index 0407897359b5e..fa0dc8b66f035 100644
--- a/clang/include/clang/Basic/LangOptions.h
+++ b/clang/include/clang/Basic/LangOptions.h
@@ -186,94 +186,10 @@ class LangOptionsBase {
 
   /// Clang versions with different platform ABI conformance.
   enum class ClangABI {
-    /// Attempt to be ABI-compatible with code generated by Clang 3.8.x
-    /// (SVN r257626). This causes <1 x long long> to be passed in an
-    /// integer register instead of an SSE register on x64_64.
-    Ver3_8,
-
-    /// Attempt to be ABI-compatible with code generated by Clang 4.0.x
-    /// (SVN r291814). This causes move operations to be ignored when
-    /// determining whether a class type can be passed or returned directly.
-    Ver4,
-
-    /// Attempt to be ABI-compatible with code generated by Clang 6.0.x
-    /// (SVN r321711). This causes determination of whether a type is
-    /// standard-layout to ignore collisions between empty base classes
-    /// and between base classes and member subobjects, which affects
-    /// whether we reuse base class tail padding in some ABIs.
-    Ver6,
-
-    /// Attempt to be ABI-compatible with code generated by Clang 7.0.x
-    /// (SVN r338536). This causes alignof (C++) and _Alignof (C11) to be
-    /// compatible with __alignof (i.e., return the preferred alignment)
-    /// rather than returning the required alignment.
-    Ver7,
-
-    /// Attempt to be ABI-compatible with code generated by Clang 9.0.x
-    /// (SVN r351319). This causes vectors of __int128 to be passed in memory
-    /// instead of passing in multiple scalar registers on x86_64 on Linux and
-    /// NetBSD.
-    Ver9,
-
-    /// Attempt to be ABI-compatible with code generated by Clang 11.0.x
-    /// (git 2e10b7a39b93). This causes clang to pass unions with a 256-bit
-    /// vector member on the stack instead of using registers, to not properly
-    /// mangle substitutions for template names in some cases, and to mangle
-    /// declaration template arguments without a cast to the parameter type
-    /// even when that can lead to mangling collisions.
-    Ver11,
-
-    /// Attempt to be ABI-compatible with code generated by Clang 12.0.x
-    /// (git 8e464dd76bef). This causes clang to mangle lambdas within
-    /// global-scope inline variables incorrectly.
-    Ver12,
-
-    /// Attempt to be ABI-compatible with code generated by Clang 14.0.x.
-    /// This causes clang to:
-    ///   - mangle dependent nested names incorrectly.
-    ///   - make trivial only those defaulted copy constructors with a
-    ///     parameter-type-list equivalent to the parameter-type-list of an
-    ///     implicit declaration.
-    Ver14,
-
-    /// Attempt to be ABI-compatible with code generated by Clang 15.0.x.
-    /// This causes clang to:
-    ///   - Reverse the implementation for DR692, DR1395 and DR1432.
-    ///   - pack non-POD members of packed structs.
-    ///   - consider classes with defaulted special member functions non-pod.
-    Ver15,
-
-    /// Attempt to be ABI-compatible with code generated by Clang 17.0.x.
-    /// This causes clang to revert some fixes to its implementation of the
-    /// Itanium name mangling scheme, with the consequence that overloaded
-    /// function templates are mangled the same if they differ only by:
-    ///   - constraints
-    ///   - whether a non-type template parameter has a deduced type
-    ///   - the parameter list of a template template parameter
-    Ver17,
-
-    /// Attempt to be ABI-compatible with code generated by Clang 18.0.x.
-    /// This causes clang to revert some fixes to the mangling of lambdas
-    /// in the initializers of members of local classes.
-    Ver18,
-
-    /// Attempt to be ABI-compatible with code generated by Clang 19.0.x.
-    /// This causes clang to:
-    ///   - Incorrectly mangle the 'base type' substitutions of the CXX
-    ///   construction vtable because it hasn't added 'type' as a substitution.
-    ///   - Skip mangling enclosing class templates of member-like friend
-    ///   function templates.
-    ///   - Ignore empty struct arguments in C++ mode for ARM, instead of
-    ///   passing them as if they had a size of 1 byte.
-    Ver19,
-
-    /// Attempt to be ABI-compatible with code generated by Clang 20.0.x.
-    /// This causes clang to:
-    ///   - Incorrectly return C++ records in AVX registers on x86_64.
-    Ver20,
-
-    /// Conform to the underlying platform's C and C++ ABIs as closely
-    /// as we can.
+#define ABI_VER_MAJOR_MINOR(Major, Minor) Ver##Major##_##Minor,
+#define ABI_VER_MAJOR(Major) Ver##Major,
+#include "clang/Basic/ABIVersions.def"
+
     Latest
   };
 
diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp
index 9f77e621a5e68..c409cbee96449 100644
--- a/clang/lib/Frontend/CompilerInvocation.cpp
+++ b/clang/lib/Frontend/CompilerInvocation.cpp
@@ -3936,45 +3936,16 @@ void CompilerInvocationBase::GenerateLangArgs(const LangOptions &Opts,
     GenerateArg(Consumer, OPT_fsanitize_ignorelist_EQ, F);
 
   switch (Opts.getClangABICompat()) {
-  case LangOptions::ClangABI::Ver3_8:
-    GenerateArg(Consumer, OPT_fclang_abi_compat_EQ, "3.8");
+#define ABI_VER_MAJOR_MINOR(Major, Minor)                                      \
+  case LangOptions::ClangABI::Ver##Major##_##Minor:                            \
+    GenerateArg(Consumer, OPT_fclang_abi_compat_EQ, #Major "." #Minor);        \
     break;
-  case LangOptions::ClangABI::Ver4:
-    GenerateArg(Consumer, OPT_fclang_abi_compat_EQ, "4.0");
-    break;
-  case LangOptions::ClangABI::Ver6:
-    GenerateArg(Consumer, OPT_fclang_abi_compat_EQ, "6.0");
-    break;
-  case LangOptions::ClangABI::Ver7:
-    GenerateArg(Consumer, OPT_fclang_abi_compat_EQ, "7.0");
-    break;
-  case LangOptions::ClangABI::Ver9:
-    GenerateArg(Consumer, OPT_fclang_abi_compat_EQ, "9.0");
-    break;
-  case LangOptions::ClangABI::Ver11:
-    GenerateArg(Consumer, OPT_fclang_abi_compat_EQ, "11.0");
-    break;
-  case LangOptions::ClangABI::Ver12:
-    GenerateArg(Consumer, OPT_fclang_abi_compat_EQ, "12.0");
-    break;
-  case LangOptions::ClangABI::Ver14:
-    GenerateArg(Consumer, OPT_fclang_abi_compat_EQ, "14.0");
-    break;
-  case LangOptions::ClangABI::Ver15:
-    GenerateArg(Consumer, OPT_fclang_abi_compat_EQ, "15.0");
-    break;
-  case LangOptions::ClangABI::Ver17:
-    GenerateArg(Consumer, OPT_fclang_abi_compat_EQ, "17.0");
-    break;
-  case LangOptions::ClangABI::Ver18:
-    GenerateArg(Consumer, OPT_fclang_abi_compat_EQ, "18.0");
-    break;
-  case LangOptions::ClangABI::Ver19:
-    GenerateArg(Consumer, OPT_fclang_abi_compat_EQ, "19.0");
-    break;
-  case LangOptions::ClangABI::Ver20:
-    GenerateArg(Consumer, OPT_fclang_abi_compat_EQ, "20.0");
+#define ABI_VER_MAJOR(Major)                                                   \
+  case LangOptions::ClangABI::Ver##Major:                                      \
+    GenerateArg(Consumer, OPT_fclang_abi_compat_EQ, #Major ".0");              \
     break;
+#include "clang/Basic/ABIVersions.def"
+
   case LangOptions::ClangABI::Latest:
     break;
   }
@@ -4482,32 +4453,16 @@ bool CompilerInvocation::ParseLangArgs(LangOptions &Opts, ArgList &Args,
                    !VerParts.second.getAsInteger(10, Minor)
              : VerParts.first.size() == Ver.size() || VerParts.second == "0")) {
       // Got a valid version number.
-      if (Major == 3 && Minor <= 8)
-        Opts.setClangABICompat(LangOptions::ClangABI::Ver3_8);
-      else if (Major <= 4)
-        Opts.setClangABICompat(LangOptions::ClangABI::Ver4);
-      else if (Major <= 6)
-        Opts.setClangABICompat(LangOptions::ClangABI::Ver6);
-      else if (Major <= 7)
-        Opts.setClangABICompat(LangOptions::ClangABI::Ver7);
-      else if (Major <= 9)
-        Opts.setClangABICompat(LangOptions::ClangABI::Ver9);
-      else if (Major <= 11)
-        Opts.setClangABICompat(LangOptions::ClangABI::Ver11);
-      else if (Major <= 12)
-        Opts.setClangABICompat(LangOptions::ClangABI::Ver12);
-      else if (Major <= 14)
-        Opts.setClangABICompat(LangOptions::ClangABI::Ver14);
-      else if (Major <= 15)
-        Opts.setClangABICompat(LangOptions::ClangABI::Ver15);
-      else if (Major <= 17)
-        Opts.setClangABICompat(LangOptions::ClangABI::Ver17);
-      else if (Major <= 18)
-        Opts.setClangABICompat(LangOptions::ClangABI::Ver18);
-      else if (Major <= 19)
-        Opts.setClangABICompat(LangOptions::ClangABI::Ver19);
-      else if (Major <= 20)
-        Opts.setClangABICompat(LangOptions::ClangABI::Ver20);
+#define ABI_VER_MAJOR_MINOR(Major_, Minor_)                                    \
+  if (std::tie(Major, Minor) <= std::tuple(Major_, Minor_))                    \
+    Opts.setClangABICompat(LangOptions::ClangABI::Ver##Major_##_##Minor_);     \
+  else
+#define ABI_VER_MAJOR(Major_)                                                  \
+  if (Major <= Major_)                                                         \
+    Opts.setClangABICompat(LangOptions::ClangABI::Ver##Major_);                \
+  else
+#include "clang/Basic/ABIVersions.def"
+      {} // sub-statement of the last else branch
     } else if (Ver != "latest") {
       Diags.Report(diag::err_drv_invalid_value)
           << A->getAsString(Args) << A->getValue();

@zwuis zwuis requested review from AaronBallman and Fznamznon August 4, 2025 16:25
Copy link

github-actions bot commented Aug 4, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

Copy link
Collaborator

@AaronBallman AaronBallman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general, I'm happy with the direction this is going, thank you!

else if (Major <= 20)
Opts.setClangABICompat(LangOptions::ClangABI::Ver20);
#define ABI_VER_MAJOR_MINOR(Major_, Minor_) \
if (std::tie(Major, Minor) <= std::tuple(Major_, Minor_)) \
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (std::tie(Major, Minor) <= std::tuple(Major_, Minor_)) \
if (std::tie(Major, Minor) <= std::tie(Major_, Minor_)) \

?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

std::tie takes lvalue (function signature: std::tuple<Ts&...> std::tie(Ts&... args)), but arguments are integer literals (ABI_VER_MAJOR_MINOR(3, 8), see ABIVersions.def).

Opts.setClangABICompat(LangOptions::ClangABI::Ver##Major_); \
else
#include "clang/Basic/ABIVersions.def"
{} // sub-statement of the last else branch
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wish we had some better way of writing this, but I've not come up with anything. :-)

Copy link
Contributor

@JustinStitt JustinStitt Aug 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The best I can think of is adding a ABI_VER_SENTINEL() to the end of the def file then we can lose the trailing {} with:

   if (Major <= Major_)                                                         \
     Opts.setClangABICompat(LangOptions::ClangABI::Ver##Major_);                \
   else
+#define ABI_VER_SENTINEL() 																										\
+      { /* Latest version - do nothing */ }
 #include "clang/Basic/ABIVersions.def"
-      {} // sub-statement of the last else branch
     } else if (Ver != "latest") {
       Diags.Report(diag::err_drv_invalid_value)
           << A->getAsString(Args) << A->getValue();

edit: your solution in a5f6dcf lgtm

Copy link
Contributor

@JustinStitt JustinStitt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once you change over to using explicit std::tie (Aaron's comment), LGTM

edit: you addressed this, we're not dealing with lvalues on the rhs 👍

@zwuis zwuis changed the title [Clang][NFC] Enumerate Clang abi versions in a separate header file [Clang][NFC] Enumerate Clang ABI versions in a separate header file Aug 5, 2025
@zwuis zwuis requested a review from AaronBallman August 6, 2025 09:41
Copy link
Contributor

@Fznamznon Fznamznon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for working on this! Honestly, I'm not a fan of .def files, but I don't have any other good ideas atm, LGTM!

@zwuis
Copy link
Contributor Author

zwuis commented Aug 7, 2025

Thank you for your review!

@zwuis zwuis merged commit 109040a into llvm:main Aug 7, 2025
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[clang] Improve ABI version handling in clang
5 participants